/*
 * localtime.c
 * Original Author:	G. Haley
 *
 * Converts the calendar time pointed to by tim_p into a broken-down time
 * expressed as local time. Returns a pointer to a structure containing the
 * broken-down time.
 */

/*
FUNCTION
<<localtime>>---convert time to local representation

INDEX
	localtime

ANSI_SYNOPSIS
	#include <time.h>
	struct tm *localtime(time_t *<[timep]>);

TRAD_SYNOPSIS
	#include <time.h>
	struct tm *localtime(<[timep]>)
	time_t *<[timep]>;

DESCRIPTION
<<localtime>> converts the time at <[timep]> into local time, then
converts its representation from the arithmetic representation to the
traditional representation defined by <<struct tm>>.

<<localtime>> constructs the traditional time representation in static
storage; each call to <<gmtime>> or <<localtime>> will overwrite the
information generated by previous calls to either function.

<<mktime>> is the inverse of <<localtime>>.

RETURNS
A pointer to the traditional time representation (<<struct tm>>).

PORTABILITY
ANSI C requires <<localtime>>.

<<localtime>> requires no supporting OS subroutines.
*/

#include <stdlib.h>
#include <time.h>

#define _SEC_IN_DAY 86400

static _CONST int _DAYS_IN_MONTH[12] =
  {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

#define _DAYS_IN_YEAR(year) (((year) % 4) ? 365 : 366)

/* Shouldn't this appear in the reent struct? */ 
static struct tm tim_s;

struct tm *
_DEFUN (localtime, (tim_p),
	_CONST time_t * tim_p)
{
  ldiv_t res;
  long days, hms;
  int dim1;
  /* compute days, hours, minutes & seconds */
  res = ldiv ((long) *tim_p, _SEC_IN_DAY);

  if (res.rem < 0)
    {
      days = res.quot - 1;
      hms = res.rem + _SEC_IN_DAY;
    }
  else
    {
      days = res.quot;
      hms = res.rem;
    }

  tim_s.tm_sec = hms % 60;
  tim_s.tm_min = (hms /= 60) % 60;
  tim_s.tm_hour = hms / 60;

  /* compute day of week */
  if ((tim_s.tm_wday = (days + 4) % 7) < 0)
    tim_s.tm_wday += 7;

  /* compute year & day of year */
  if (days >= 0)
    {
      for (tim_s.tm_year = 70; days >= _DAYS_IN_YEAR (tim_s.tm_year);
	   tim_s.tm_year++)
	days -= _DAYS_IN_YEAR (tim_s.tm_year);
    }
  else
    {
      for (tim_s.tm_year = 70; days < 0; tim_s.tm_year--)
	days += _DAYS_IN_YEAR (tim_s.tm_year);
    }
  tim_s.tm_yday = days;

  /* compute month & day of month */
  if (_DAYS_IN_YEAR (tim_s.tm_year) == 366)
    dim1 = 29;
  else
    dim1 = 28;

  for (tim_s.tm_mon = 0;
       days >= _DAYS_IN_MONTH[tim_s.tm_mon];
       tim_s.tm_mon++)
    {
      if (tim_s.tm_mon == 1)
	{
	  days -= dim1;
	}
      else
	{
	  days -= _DAYS_IN_MONTH[tim_s.tm_mon];
	}
    }
  tim_s.tm_mday = days + 1;

  /* set Daylight Saving Time flag */
  tim_s.tm_isdst = -1;

  return (&tim_s);
}
